﻿///////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2013 Laszlo Arvai. All rights reserved.
//
// This library is free software; you can redistribute it and/or modify it 
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, 
// or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
// MA 02110-1301  USA
///////////////////////////////////////////////////////////////////////////////
// File description
// ----------------
// Settings handler class
///////////////////////////////////////////////////////////////////////////////
using Microsoft.Win32;
using System;
using System.IO;
using System.Reflection;
using System.Xml;

namespace CygnusGroundStation
{
  public class SystemSettings
  {
    #region · Data members ·

    XmlDocument m_xml_doc;
    string m_filename;
    string m_rootname;
    bool m_changed;
    private static SystemSettings m_default = null;

    #endregion

    #region · Constructor ·
    // Default constructor
    public SystemSettings()
    {
    }
    #endregion

    #region · Singleton members ·
    /// <summary>
    /// Gets default singleton instance
    /// </summary>
    public static SystemSettings Default
    {
      get
      {
        if (m_default == null)
        {
          m_default = new SystemSettings();
        }

        return m_default;
      }
    }
    #endregion

    #region · Properties ·

    public string FileName
    {
      get { return m_filename; }
      set { m_filename = value; }
    }

    public bool IsChanged
    {
      get { return m_changed; }
    }

    public string GetRootName
    {
      get { return m_rootname; }
    }

    #endregion

    #region Public members

    /// <summary>
    /// Loads settings file. Filename is generated automatically either from the registry entry or exe name.
    /// </summary>
    /// <returns></returns>
    public bool Load()
    {
      // Generate filename
      m_filename = GetConfigFileName();

      if (m_filename != string.Empty)
      {
        try
        {
          m_xml_doc = new XmlDocument();

          m_xml_doc.Load(m_filename);

          m_changed = false;

          m_rootname = m_xml_doc.DocumentElement.Name;

          return true;
        }
        catch
        {
          CreateSettingsDocument();
          return false;
        }
      }
      else
      {
        CreateSettingsDocument();
        return false;
      }
    }

    /// <summary>
    /// Saves file
    /// </summary>
    public void Save()
    {
      try
      {
        m_xml_doc.Save(m_filename);

        m_changed = false;
      }
      catch (Exception ex)
      {
        //MessageBox.Show(ex.Message);
      }
    }

    /// <summary>
    /// Deletes all entries of a section
    /// </summary>
    /// <param name="in_section"></param>
    public void ClearSection(string in_section)
    {
      XmlNode root = m_xml_doc.DocumentElement;
      XmlNode s = root.SelectSingleNode('/' + m_rootname + '/' + in_section);

      if (s == null)
        return;  //not found

      s.RemoveAll();

      m_changed = true;
    }

    /// <summary>
    /// Returns a list of all section names
    /// </summary>
    /// <returns></returns>
    public string[] GetSections()
    {
      XmlNode root = m_xml_doc.DocumentElement;
      XmlNodeList nodeList = root.SelectNodes('/' + m_rootname + "/*");
      string[] retval = new string[nodeList.Count];

      // Get names
      for (int i = 0; i < nodeList.Count; i++)
      {
        retval[i] = nodeList[i].Name;
      }

      return retval;
    }

    /// <summary>
    /// Returns the list of all keys in a section.
    /// </summary>
    /// <param name="in_section_name"></param>
    /// <returns></returns>
    public string[] GetKeys(string in_section_name)
    {
      XmlNode root = m_xml_doc.DocumentElement;
      XmlNodeList nodeList = root.SelectNodes('/' + m_rootname + '/' + in_section_name + "/*");
      string[] retval = new string[nodeList.Count];

      // Get names
      for (int i = 0; i < nodeList.Count; i++)
      {
        retval[i] = nodeList[i].Name;
      }

      return retval;
    }

    /// <summary>
    /// Removes a section and all of its entries
    /// </summary>
    /// <param name="section"></param>
    public void RemoveSection(string section)
    {
      XmlNode root = m_xml_doc.DocumentElement;
      XmlNode s = root.SelectSingleNode('/' + m_rootname + '/' + section);

      if (s != null)
        root.RemoveChild(s);
    }

    #endregion

    #region · Non-public members ·

    //initializes a new settings file with the XML version
    //and the root node
    protected void CreateSettingsDocument()
    {
      m_xml_doc.AppendChild(m_xml_doc.CreateXmlDeclaration("1.0", null, null));
      m_xml_doc.AppendChild(m_xml_doc.CreateElement("settings"));
    }
    #endregion

    #region Read methods

    public bool ReadBool(string section, string name, bool defaultValue)
    {
      string s = ReadString(section, name, "");

      if (s == Boolean.TrueString)
        return true;
      else if (s == Boolean.FalseString)
        return false;
      else
        return defaultValue;
    }

    public DateTime ReadDateTime(string section, string name, DateTime defaultValue)
    {
      string s = ReadString(section, name, "");

      if (s == "")
        return defaultValue;
      else
      {
        try
        {
          DateTime dt = Convert.ToDateTime(s);
          return dt;
        }
        catch (FormatException)
        {
          return defaultValue;
        }
      }
    }

    public double ReadDouble(string section, string name, double defaultValue)
    {
      string s = ReadString(section, name, "");

      if (s == "")
        return defaultValue;
      else
      {
        try
        {
          double d = Convert.ToDouble(s);
          return d;
        }
        catch (FormatException)
        {
          return defaultValue;
        }
      }
    }

    public float ReadFloat(string section, string name, float defaultValue)
    {
      string s = ReadString(section, name, "");

      if (s == "")
        return defaultValue;
      else
      {
        try
        {
          float f = Convert.ToSingle(s);
          return f;
        }
        catch (FormatException)
        {
          return defaultValue;
        }
      }
    }

    public int ReadInt(string section, string name, int defaultValue)
    {
      string s = ReadString(section, name, "");

      if (s == "")
        return defaultValue;
      else
      {
        try
        {
          int n = Convert.ToInt32(s);
          return n;
        }
        catch (FormatException)
        {
          return defaultValue;
        }
      }
    }

    public long ReadLong(string section, string name, long defaultValue)
    {
      string s = ReadString(section, name, "");

      if (s == "")
        return defaultValue;
      else
      {
        try
        {
          long l = Convert.ToInt64(s);
          return l;
        }
        catch (FormatException)
        {
          return defaultValue;
        }
      }
    }

    public short ReadShort(string section, string name, short defaultValue)
    {
      string s = ReadString(section, name, "");

      if (s == "")
        return defaultValue;
      else
      {
        try
        {
          short n = Convert.ToInt16(s);
          return n;
        }
        catch (FormatException)
        {
          return defaultValue;
        }
      }
    }

    public string ReadString(string section, string name, string defaultValue)
    {
      XmlNode root = m_xml_doc.DocumentElement;
      XmlNode s = root.SelectSingleNode('/' + m_rootname + '/' + section);

      if (s == null)
        return defaultValue;  //not found

      XmlNode n = s.SelectSingleNode(name);

      if (n == null)
        return defaultValue;  //not found

      XmlAttributeCollection attrs = n.Attributes;

      foreach (XmlAttribute attr in attrs)
      {
        if (attr.Name == "value")
          return attr.Value;
      }

      return defaultValue;
    }

    public uint ReadUInt(string section, string name, uint defaultValue)
    {
      string s = ReadString(section, name, "");

      if (s == "")
        return defaultValue;
      else
      {
        try
        {
          uint n = Convert.ToUInt32(s);
          return n;
        }
        catch (FormatException)
        {
          return defaultValue;
        }
      }
    }

    public ulong ReadULong(string section, string name, ulong defaultValue)
    {
      string s = ReadString(section, name, "");

      if (s == "")
        return defaultValue;
      else
      {
        try
        {
          ulong l = Convert.ToUInt64(s);
          return l;
        }
        catch (FormatException)
        {
          return defaultValue;
        }
      }
    }

    public ushort ReadUShort(string section, string name, ushort defaultValue)
    {
      string s = ReadString(section, name, "");

      if (s == "")
        return defaultValue;
      else
      {
        try
        {
          ushort n = Convert.ToUInt16(s);
          return n;
        }
        catch (FormatException)
        {
          return defaultValue;
        }
      }
    }

    #endregion

    #region Write methods

    public void WriteBool(string section, string name, bool value)
    {
      WriteString(section, name, value.ToString());
    }

    public void WriteDateTime(string section, string name, DateTime value)
    {
      WriteString(section, name, value.ToString());
    }

    public void WriteDouble(string section, string name, double value)
    {
      WriteString(section, name, value.ToString());
    }

    public void WriteFloat(string section, string name, float value)
    {
      WriteString(section, name, value.ToString());
    }

    public void WriteInt(string section, string name, int value)
    {
      WriteString(section, name, value.ToString());
    }

    public void WriteLong(string section, string name, long value)
    {
      WriteString(section, name, value.ToString());
    }

    public void WriteShort(string section, string name, short value)
    {
      WriteString(section, name, value.ToString());
    }

    public void WriteString(string section, string name, string value)
    {
      XmlNode root = m_xml_doc.DocumentElement;
      XmlNode s = root.SelectSingleNode('/' + m_rootname + '/' + section);

      if (s == null)
        s = root.AppendChild(m_xml_doc.CreateElement(section));

      XmlNode n = s.SelectSingleNode(name);

      if (n == null)
        n = s.AppendChild(m_xml_doc.CreateElement(name));

      XmlAttribute attr = ((XmlElement)n).SetAttributeNode("value", "");
      attr.Value = value;

      m_changed = true;
    }

    public void WriteUInt(string section, string name, uint value)
    {
      WriteString(section, name, value.ToString());
    }

    public void WriteULong(string section, string name, ulong value)
    {
      WriteString(section, name, value.ToString());
    }

    public void WriteUShort(string section, string name, ushort value)
    {
      WriteString(section, name, value.ToString());
    }

    #endregion

    #region Static members
    static public string GetConfigFileName()
    {
      RegistryKey software = Registry.CurrentUser.OpenSubKey("Software");
      RegistryKey cygnus_ground_station = software.OpenSubKey("CygnusGroundStation");

      if (cygnus_ground_station != null && cygnus_ground_station.GetValue("SettingsFile") != null)
        return cygnus_ground_station.GetValue("SettingsFile").ToString();
      else
        return Path.ChangeExtension(Assembly.GetExecutingAssembly().Location, "config"); ;
    }
    #endregion
  }
}

